/******************************************************************************************
 *   Obsługa wyświetlacza LCD (hd44780.c)
 *
 *   Utworzono: 11-01-2011
 *       Autor: czopas
 *****************************************************************************************/
#include "hd44780.h"

/******************************************************************************************
 *	Makra definiujące sygnały sterujące RS, RW oraz E						 			 *
 *****************************************************************************************/
#define SET_RS 	LCD_RS_PORT |= LCD_RS	// stan wysoki na linii RS
#define CLR_RS 	LCD_RS_PORT &= ~LCD_RS	// stan niski na linii RS

#define SET_RW 	LCD_RW_PORT |= LCD_RW	// stan wysoki na RW - odczyt z LCD
#define CLR_RW 	LCD_RW_PORT &= ~LCD_RW	// stan niski na RW - zapis do LCD

#define SET_E 	LCD_E_PORT |= LCD_E		// stan wysoki na linii E
#define CLR_E 	LCD_E_PORT &= ~LCD_E	// stan niski na linii E

/*
 * 	Funkcja ustawiająca porty danych LCD w kierunku wyjściowym
 */
static inline void _lcd_data_dir_out(void)
{
	LCD_DB7_DIR	|= LCD_DB7;
	LCD_DB6_DIR	|= LCD_DB6;
	LCD_DB5_DIR	|= LCD_DB5;
	LCD_DB4_DIR	|= LCD_DB4;
}/* _lcd_data_dir_out */

/*
 * 	Funkcja ustawiająca porty danych LCD w kierunku wejściowym
 */
#if USE_RW == 1
static inline void _lcd_data_dir_in(void)
{
	LCD_DB7_DIR	&= ~LCD_DB7;
	LCD_DB6_DIR	&= ~LCD_DB6;
	LCD_DB5_DIR	&= ~LCD_DB5;
	LCD_DB4_DIR	&= ~LCD_DB4;
}/* _lcd_data_dir_in */
#endif

/*
 * 	Funkcja zapisująca półbajt danych na magistralę danych
 *  Argument: data - półbajt do wysłania do wyświetlacza
 */
static inline void _lcd_write_half_byte(uint8_t data)
{
	if (data&(1<<0)) LCD_DB4_PORT |= LCD_DB4; else LCD_DB4_PORT &= ~LCD_DB4;
	if (data&(1<<1)) LCD_DB5_PORT |= LCD_DB5; else LCD_DB5_PORT &= ~LCD_DB5;
	if (data&(1<<2)) LCD_DB6_PORT |= LCD_DB6; else LCD_DB6_PORT &= ~LCD_DB6;
	if (data&(1<<3)) LCD_DB7_PORT |= LCD_DB7; else LCD_DB7_PORT &= ~LCD_DB7;
}/* _lcd_write_half_byte */

/*
 * 	Funkcja odczytu półbajtu danych z magistrali danych
 *  Wynik: półbajt odczytany z magistrali
 */
#if USE_RW == 1
static inline uint8_t _lcd_read_half_byte(void)
{
	uint8_t result=0;

	if(LCD_DB4_PIN & LCD_DB4) result |= (1<<0);
	if(LCD_DB5_PIN & LCD_DB5) result |= (1<<1);
	if(LCD_DB6_PIN & LCD_DB6) result |= (1<<2);
	if(LCD_DB7_PIN & LCD_DB7) result |= (1<<3);

	return result;
}/* _lcd_read_half_byte */
#endif

/*
 * 	Funkcja odczytu bajtu danych z wyświetlacza
 *  Wynik: bajt odczytany z wyświetlacza
 */
#if USE_RW == 1
uint8_t _lcd_read_byte(void)
{
	uint8_t result=0;
	_lcd_data_dir_in();

	SET_RW;

	SET_E;
	result = (_lcd_read_half_byte() << 4);	// odczyt starszej części bajtu z LCD D7...D4
	CLR_E;

	SET_E;
	result |= _lcd_read_half_byte();			// odczyt młodszej części bajtu z LCD D3...D0
	CLR_E;

	return result;
}/* _lcd_read_byte */
#endif

/*
 * 	Funkcja odczytująca flagę zajętości wyświetlacza
 *  Wynik: 0 lub 1 flaga zajętości
 */
#if USE_RW == 1
uint8_t _lcd_check_busy_flag(void)
{
	CLR_RS;
	return _lcd_read_byte();
}/* _lcd_check_busy_flag */
#endif

/*
 * 	Funkcja zapisująca bajt danych do wyświetlacza
 *  Argument: _data - półbajt do wysłania do wyświetlacza
 */
void _lcd_write_byte(unsigned char _data)
{
	_lcd_data_dir_out();	// ustawienie pinów portu LCD D4...D7 jako wyjścia

#if USE_RW == 1
	CLR_RW;
#endif

	SET_E;
	_lcd_write_half_byte(_data >> 4);	// wysłanie starszej części bajtu danych D7...D4
	CLR_E;

	SET_E;
	_lcd_write_half_byte(_data);		// wysłanie młodszej części bajtu danych D3...D0
	CLR_E;

#if USE_RW == 1
	while((_lcd_check_busy_flag() & (1<<7)));
#else
	_delay_us(120);
#endif
}/* _lcd_write_byte */

/*
 * 	Funkcja zapisująca komendę do wyświetlacza
 *  Argument: command - komenda do zapisu
 */
void lcd_write_command(uint8_t command)
{
	CLR_RS;
	_lcd_write_byte(command);
}/* lcd_write_command */

/*
 * 	Funkcja zapisująca dane do wyświetlacza
 *  Argument: data - dane do zapisu
 */
void lcd_write_data(uint8_t data)
{
	SET_RS;
	_lcd_write_byte(data);
}/* lcd_write_data */

/*
 * 	Funkcja wyświetlająca znak na wyświetlaczu
 *  Argument: c - znak do wyświetlenia
 */
#if USE_LCD_CHAR == 1
void lcd_write_char(char c)
{
	lcd_write_data( ( c>=0x80 && c<=0x87 ) ? (c & 0x07) : c);
}/* lcd_write_char */
#endif

/*
 * 	Funkcja zapisująca tekst z pamięci RAM do wyświetlacza
 *  Argument: *str - wskaźnik do stringa
 */
void lcd_write_text(char * str)
{
	register char znak;
	while ( (znak=*(str++)) )
		lcd_write_data( ( znak>=0x80 && znak<=0x87 ) ? (znak & 0x07) : znak);
}/* lcd_write_text */

/*
 * 	Funkcja zapisująca tekst z pamięci FLASH do wyświetlacza
 *  Argument: *str - wskaźnik do stringa
 */
#if USE_LCD_STR_P == 1
void lcd_write_text_P(const char * str)
{
	register char znak;
	while ( (znak=pgm_read_byte(str++)) )
		lcd_write_data( ( (znak>=0x80) && (znak<=0x87) ) ? (znak & 0x07) : znak);
}/* lcd_write_text_P */
#endif

/*
 * 	Funkcja zapisująca tekst z pamięci EEPROM do wyświetlacza
 *  Argument: *str - wskaźnik do stringa
 */
#if USE_LCD_STR_E == 1
void lcd_write_text_E(char * str)
{
	register char znak;
	while(1)
	{
		znak=eeprom_read_byte( (uint8_t *)(str++) );
		if(!znak || znak==0xFF) break;
		else lcd_write_data( ( (znak>=0x80) && (znak<=0x87) ) ? (znak & 0x07) : znak);
	}
}/* lcd_write_text_E */
#endif

/*
 * 	Funkcja wyświetlająca liczbę dziesiętną na wyświetlaczu
 *  Argument: val - liczba do wyświetlenia
 *  		  zero - określenie czy ma zostać dodane 0 przed liczbą mniejszą niż 10
 */
#if USE_LCD_INT == 1
void lcd_write_int(int val, uint8_t zero)
{
	char bufor[17];
	if((val < 10)){
		switch (zero){
			case 0x01:	lcd_write_text_P(PSTR("0"));
						break;
			case 0x02:	lcd_write_text_P(PSTR(" "));
						break;
		}
	}
	lcd_write_text(itoa(val, bufor, 10));
}/* lcd_write_int */
#endif

/*
 * 	Funkcja wyświetlająca liczbę szesnastkową na wyświetlaczu
 *  Argument: val - liczba do wyświetlenia
 */
#if USE_LCD_HEX == 1
void lcd_write_hex(int val)
{
	char bufor[17];
	lcd_write_text_P(PSTR("0x"));
	if(val <= 0x0F)
		lcd_write_text_P(PSTR("0"));
	lcd_write_text(itoa(val, bufor, 16));
}/* lcd_write_hex */
#endif

/*
 * 	Funkcja definiująca znak na LCD z pamięci RAM
 *  Argument: nr - kod znaku w pamięci CGRAM (0x80...0x87)
 *  		  *znak - wskaźnik do tablicy 7-bajtowej definiującej znak
 */
#if USE_LCD_DEFCHAR == 1
void lcd_define_char(uint8_t nr, uint8_t *znak)
{
	register uint8_t i,c;
	lcd_write_command(64+((nr&0x07)*8));
	for(i=0;i<8;i++)
	{
		c = *(znak++);
		lcd_write_data(c);
	}
}/* lcd_define_char */
#endif

/*
 * 	Funkcja definiująca znak na LCD z pamięci FLASH
 *  Argument: nr - kod znaku w pamięci CGRAM (0x80...0x87)
 *  		  *znak - wskaźnik do tablicy 7-bajtowej definiującej znak
 */
#if USE_LCD_DEFCHAR_P == 1
void lcd_define_char_P(uint8_t nr, const uint8_t *znak)
{
	register uint8_t i,c;
	lcd_write_command(64+((nr&0x07)*8));
	for(i=0;i<8;i++)
	{
		c = pgm_read_byte(znak++);
		lcd_write_data(c);
	}
}/* lcd_define_char_P */
#endif

/*
 * 	Funkcja definiująca znak na LCD z pamięci EEPROM
 *  Argument: nr - kod znaku w pamięci CGRAM (0x80...0x87)
 *  		  *znak - wskaźnik do tablicy 7-bajtowej definiującej znak
 */
#if USE_LCD_DEFCHAR_E == 1
void lcd_define_char_E(uint8_t nr, uint8_t *znak)
{
	register uint8_t i,c;

	lcd_write_command(64+((nr&0x07)*8));
	for(i=0;i<8;i++)
	{
		c = eeprom_read_byte(znak++);
		lcd_write_data(c);
	}
}/* lcd_define_char_E */
#endif

/*
 * 	Funkcja ustawiająca kursor w wybranym miejscu wyświetlacza
 *  Argument: x - współrzędna x, y - współrzędna y
 */
#if USE_LCD_GO_TO == 1
void lcd_go_to(uint8_t x, uint8_t y)
{
	switch(y)
	{
		case 0: y = LCD_LINE1; break;

#if (LCD_Y>1)
	    case 1: y = LCD_LINE2; break; // adres 1 znaku 2 wiersza
#endif
#if (LCD_Y>2)
    	case 2: y = LCD_LINE3; break; // adres 1 znaku 3 wiersza
#endif
#if (LCD_Y>3)
    	case 3: y = LCD_LINE4; break; // adres 1 znaku 4 wiersza
#endif
	}

	lcd_write_command((0x80 + y + x));
}/* lcd_go_to */
#endif

/*
 * 	Funkcja czyszcząca zawartość wyświetlacza
 */
void lcd_clear(void)
{
	lcd_write_command(HD44780_CLEAR);

	#if USE_RW == 0
		_delay_ms(4.9);
	#endif
}/* lcd_clear */

/*
 * 	Funkcja ustawiająca kursor w miejscu początkowym wyświetlacza
 */
#if USE_LCD_CURSOR_HOME == 1
void lcd_home(void)
{
	lcd_write_command(HD44780_CLEAR|HD44780_HOME);

	#if USE_RW == 0
		_delay_ms(4.9);
	#endif
}/* lcd_home */
#endif

/*
 * 	Funkcja włączająca kursor na wyświetlaczu
 */
#if USE_LCD_CURSOR_ON == 1
void lcd_cursor_on(void)
{
	lcd_write_command(HD44780_DISPLAY_ONOFF|HD44780_DISPLAY_ON|HD44780_CURSOR_ON);
}/* lcd_cursor_on */

/*
 * 	Funkcja wyłączająca kursor na wyświetlaczu
 */
void lcd_cursor_off(void)
{
	lcd_write_command(HD44780_DISPLAY_ONOFF|HD44780_DISPLAY_ON);
}/* lcd_cursor_off */
#endif

/*
 * 	Funkcja włączająca miganie kursora na wyświetlaczu
 */
#if USE_LCD_CURSOR_BLINK == 1
void lcd_blink_on(void)
{
	lcd_write_command(HD44780_DISPLAY_ONOFF|HD44780_DISPLAY_ON|HD44780_CURSOR_ON|HD44780_CURSOR_BLINK);
}/* lcd_blink_on */

/*
 * 	Funkcja wyłączająca miganie kursora na wyświetlaczu
 */
void lcd_blink_off(void)
{
	lcd_write_command(HD44780_DISPLAY_ONOFF|HD44780_DISPLAY_ON);
}/* lcd_blink_off */
#endif

/*
 * 	Funkcja inicjalizująca wyświetlacz do pracy
 */
void lcd_init(void)
{
	_lcd_data_dir_out();
	LCD_RS_DIR |= LCD_RS;
	LCD_E_DIR |= LCD_E;
	#if USE_RW == 1
	LCD_RW_DIR |= LCD_RW;
	#endif

	LCD_RS_PORT &= ~LCD_RS;
	LCD_E_PORT &= ~LCD_E;
	#if USE_RW == 1
		LCD_RW_PORT &= ~LCD_RW;
	#endif

	_delay_ms(15);
	LCD_RS_PORT &= ~LCD_RS;
#if USE_RW == 1
	LCD_RW_PORT &= ~LCD_RW;
#endif

	_lcd_write_half_byte(HD44780_FUNCTION_SET|HD44780_8_BIT);
	_delay_ms(4.1);
	_lcd_write_half_byte(HD44780_FUNCTION_SET|HD44780_8_BIT);
	_delay_us(100);
	_lcd_write_half_byte(HD44780_FUNCTION_SET|HD44780_4_BIT);
	_delay_us(100);

	lcd_write_command(HD44780_FUNCTION_SET|HD44780_8_BIT|HD44780_TWO_LINE|HD44780_FONT5x7);
	lcd_write_command(HD44780_DISPLAY_ONOFF|HD44780_CURSOR_OFF);
	lcd_write_command(HD44780_DISPLAY_ONOFF|HD44780_DISPLAY_ON);
	lcd_write_command(HD44780_ENTRY_MODE|HD44780_EM_INCREMENT);

	lcd_clear();
}/* lcd_init */

/******************************************************************************************
 *	Koniec pliku hd44780.c
 *****************************************************************************************/
